package com.jse;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.HexFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.regex.Pattern;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.jse.json.Json;
import com.jse.json.JsonObject;

@SuppressWarnings("rawtypes")
public class Lang {

	final static String RAND_STRING="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";//_-
	final static RandomGenerator splittableRandom = RandomGeneratorFactory.of("SplittableRandom").create();//单线程最快
	public static RandomGenerator rand() {return splittableRandom;}
	public static int rand(int max) {
		return rand().nextInt(0,max+1);
	}
	public static int rand(int min,int max) {
		return rand().nextInt(min,max+1);
	}
	public static String randomString(int len) {
		return randomString(RAND_STRING, len);
	}
	public static String randomString(String s, int len) {
		if (s==null||s.isEmpty()) {
			return "";
		}
		final StringBuilder sb = new StringBuilder(len);
		if (len < 1) {
			len = 1;
		}
		int baseLength = s.length();
		for (int i = 0; i < len; i++) {
			int number = rand(baseLength-1);
			sb.append(s.charAt(number));
		}
		return sb.toString();
	}
	
	public static boolean startsWith(String s,Collection<String> c) {
		return c.parallelStream().anyMatch(s::startsWith);
	}
	public static String startsWithGet(String s,Collection<String> c) {
		return c.parallelStream().filter(s::startsWith).findAny().orElse(null);
	}

	public static boolean endsWith(String s, Set<String> c) {
		return c.parallelStream().anyMatch(s::endsWith);
	}

	public static boolean notNull(Object...args){return Arrays.stream(args).noneMatch(Lang::isEmpty);}

	public static boolean isEmpty(Object x) {
		if(x==null)return true;
		else if(x instanceof Collection c)return c.isEmpty();
		else if(x instanceof Map m)return m.isEmpty();
		else if(x.getClass().isArray())return Array.getLength(x)==0;
		else if(x instanceof Iterator i)return i.hasNext();
		else if(x instanceof Iterable i)return i.iterator().hasNext();
		else if(x instanceof CharSequence s)return s.isEmpty()||s.equals("null")||s.equals("undefined");
		else if(x.getClass().getName().endsWith("nashorn.internal.runtime.Undefined"))return true;
		else return false;
	}
	
	public static String uuid() {return UUID.randomUUID().toString().replace("-","");}
	public static String uuid36() {return UUID.randomUUID().toString();}
	
	private static long snowflake_timestamp=System.currentTimeMillis();
    private static long snowflake_sequence = 0;//序列
    
    public static long snowflake() {return snowflake(0);}
    /**
     * 返回下一个key. 注意服务器时间不要调整
     * @return 下一个key
     */
    public static synchronized long snowflake(int node) throws RuntimeException {
        snowflake_sequence++;
        if (snowflake_sequence > 4095) {//当前时间小于1693843249454 2023年小于当前时间 时间不对服务器调整了 
            snowflake_timestamp = System.currentTimeMillis();//3882312855551L 2093年最大时间戳超过70年
            snowflake_sequence = 0;
        }//此算法编码完成的时间 1523289600000L 
        return ((snowflake_timestamp - 1523289600000L) << 22) | (node << 12) | snowflake_sequence;
    }
    
	public static <T> Map ofMap(Object...args) {
		JsonObject m=new JsonObject();
		for (int i = 0; i < args.length; i++) {
			m.put(args[i].toString(),args[i+1]);
			i++;
		}
		return m;
	}
	
	public static <T> Set ofSet(Object...args){return new HashSet<Object>(Arrays.asList(args));}
	
	public static byte[] serialize(Serializable obj) throws IOException {
        var baos = new ByteArrayOutputStream(512);
        try(var out=new ObjectOutputStream(baos)){out.writeObject(obj);}
        return baos.toByteArray();
    }

    public static Object deserialize(byte[] bytes) throws ClassNotFoundException, IOException {
        var bais = new ByteArrayInputStream(bytes);
        try(var in =new ObjectInputStream(bais)){return in.readObject();}
    }

	public final static Map<String,MessageDigest> mds = new ConcurrentHashMap<>();
	
	public static String md5(Object s) {return digest("md5", s);}
	public static byte[] hmacSHA256(String secret, String message) throws Exception {
//        String hash = "";
        Mac hmacSha256 = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
        hmacSha256.init(secret_key);
//        byte[] bytes = hmacSha256.doFinal(message.getBytes(StandardCharsets.UTF_8));
//        hash = hmacSha256byteArrayToHexString(bytes);
//        return hash;
        return hmacSha256.doFinal(message.getBytes(StandardCharsets.UTF_8));
    }
	/**
     * 将加密后的字节数组转换成16进制字符串
     * @param b 字节数组
     * @return 字符串
     */
    public  static String byteArrayToHexString(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b!=null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }
        return hs.toString().toLowerCase();
    }
	
	public static String digest(String algorithm,Object o){
		if(o instanceof File f){
			try {
				return digest(algorithm,new FileInputStream(f));
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			}
		}else if(o instanceof Path p){
			try {
				return digest(algorithm,new FileInputStream(p.toFile()));
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			}
		}else if(o instanceof InputStream in){//大文件流计算
			try(in){
				MessageDigest md = MessageDigest.getInstance(algorithm);
				byte[] bs = new byte[16 * 1024];
				int len = 0;
				while ((len = in.read(bs)) != -1) {
					md.update(bs, 0, len);
				}
				return HexFormat.of().formatHex(md.digest());
			} catch (NoSuchAlgorithmException | FileNotFoundException e) {
				throw new RuntimeException(e);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}else if(o instanceof byte[] bs){
			return digest(algorithm,bs,null,1);
		}
		return digest(algorithm,o.toString().getBytes(StandardCharsets.UTF_8),null,1);
	}
	/**
	 * 从字节数组计算出数字签名
	 *
	 * @param algorithm  算法，比如 "SHA1" 或者 "MD5" 等
	 * @param bytes      字节数组
	 * @param salt       随机字节数组
	 * @param iterations 迭代次数
	 * @return 数字签名
	 */
	public static String digest(String algorithm, byte[] bytes, byte[] salt, int iterations) {
		try {
			MessageDigest md = mds.get(algorithm);
			if(md==null) {
				md=MessageDigest.getInstance(algorithm);
				mds.put(algorithm, md);
			}
			if (salt != null) {
				md.update(salt);
			}
			byte[] hashBytes = md.digest(bytes);
			for (int i = 1; i < iterations; i++) {
				md.reset();
				hashBytes = md.digest(hashBytes);
			}
			return HexFormat.of().formatHex(hashBytes);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}
	}
	public static String format(Object o,Object a) {
		if(o==null&&a==null)return null;
		if(o==null)return a.toString();
		if(a==null)return o.toString();
		if(o instanceof Date d)return Times.format(a.toString(),d);
		if(o instanceof Number d) {return new DecimalFormat(a.toString()).format(d);}
		if(o instanceof String s) {
			if(a instanceof Date d)return Times.format(s,d);
			if(a.getClass().isArray())return s.formatted((Object[])a);
			return s.formatted(a);
		}
		return o.toString();
	}
	public static String timeId(String s) {return Times.format("yyyyMMddHHmmssSSS").concat(s);}
	public static Object def(Object o, Object d) {if(isEmpty(o))return d;return o;}
	public static Date now() {return new Date();}
	public static Object json(Object o){if(o==null)return "[]";if(o instanceof String s)return Json.parse(s);return Json.toJson(o);}
	public static boolean isMobile(String ua) {return ua!=null&&(isAndroid(ua)||isIOS(ua));}
	public static boolean isIOS(String ua){ua=ua.toLowerCase();return ua.contains("iphone")||ua.contains("ipad")||ua.contains("ipod");}
	public static boolean isAndroid(String ua) {return ua.toLowerCase().contains("android");}
	public static boolean isWeChat(String ua) {return ua.toLowerCase().contains("micromessenger");}
	
	public static <T> T mapToBean(Map map,Class<T> beanClass) {
		try {
			T obj = beanClass.newInstance();
			BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				if (!map.containsKey(key)) {
					continue;
				}
				Object value = map.get(key);
				Method setter = property.getWriteMethod();// 得到property对应的setter方法
				setter.invoke(obj, value);
			}
			return obj;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 对象转map
	 *
	 * @param bean 对象
	 * @return map
	 */
	public static JsonObject beanToMap(Object bean) {
		return new JsonObject(bean);
	}
	public static JsonObject javabeanToMap(Object obj) {
		if (obj == null) {
			return null;
		}
		JsonObject map = new JsonObject();
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				if (key.equals("class")) {// 过滤class属性
					continue;
				}
				Method getter = property.getReadMethod();// 得到property对应的getter方法
				Object value = getter.invoke(obj);
				map.put(key, value);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return map;
	}
	public static Map<String,String> smap(Object o) {
		if(o instanceof Map m) {
			var mp=new HashMap<String,String>();
			m.forEach((k,v)->{
				mp.put(k.toString(),v!=null?v.toString():"");
			});
			return mp;
		}
		return null;
	}
	
	/**
	 * number保留两位小数
	 * @param n
	 * @return
	 */
	public static double bits2(Number n) {
		return Double.parseDouble(String.format("%.2f",n));
	}
	
	public static int parseInt(Object o) {
		if(o==null) {
			return -1;
		}else if(o instanceof Number n) {
			return n.intValue();
		}
		String s=o.toString();
		if(s.indexOf('.')!=-1) {
			s=s.substring(0,s.indexOf('.'));
		}
		return Integer.parseInt(s);
	}
	
	public static float parseFloat(Object o) {
		if(o==null) {
			return -1.0f;
		}else if(o instanceof Number n) {
			return n.floatValue();
		}
		return Float.parseFloat(o.toString());
	}
	private static final Map<String,Pattern> patterns=new HashMap<>();
	
	public static Pattern pattern(String regex) {
		if(patterns.containsKey(regex)) return patterns.get(regex);
		else {var pattern=Pattern.compile(regex);patterns.put(regex,pattern);return pattern;}
	}
	
	public static boolean match(String regex, String value) {
		return pattern(regex).matcher(value).find();
	}
	
	public static void main(String[] args) {
		System.out.println(format("%.2f",222.1));
	}
	
	
	
}
